package jmine.tec.security.ldap;

import java.util.Collections;
import java.util.Set;

import javax.security.auth.Subject;

import jmine.tec.security.ldap.connector.LdapConnector;
import jmine.tec.security.ldap.exception.AuthenticationException;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.ldap.core.LdapTemplate;

import bancosys.tec.security.SecurityException;
import bancosys.tec.security.authorization.Permission;
import bancosys.tec.security.impl.AbstractSecurityManager;
import bancosys.tec.security.impl.SecurityPrincipal;
import bancosys.tec.security.impl.dao.CredentialDAO;
import bancosys.tec.security.impl.web.WebSecurityContext;
import bancosys.tec.security.impl.web.WebSecurityManager;
import bancosys.tec.security.impl.web.manager.HttpApplicationSecurityManager;

/**
 * Autenticação e autorização via LDAP.
 * 
 * @created 16 de setembro de 2010
 * @author seiti
 */
public class LdapSecurityManager extends AbstractSecurityManager<WebSecurityContext> implements WebSecurityManager {

    private static final Logger LOG = LogManager.getLogger(LdapSecurityManager.class);

    private LoginDelegate delegate;

    /**
     * Responsável por consultar o LDAP
     */
    private LdapTemplate ldapTemplate;

    /**
     * Encapsula os filtros de consulta ao LDAP
     */
    private LdapConnector connector;

    /**
     * Sets the ldaptemplate
     * 
     * @param ldapTemplate the ldapTemplate
     */
    public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }

    /**
     * Return the ldaptemplate
     * 
     * @return the ldapTemplate
     */
    public LdapTemplate getLdapTemplate() {
        return this.ldapTemplate;
    }

    /**
     * Sets the connector
     * 
     * @param connector the connector
     */
    public void setConnector(LdapConnector connector) {
        this.connector = connector;
    }

    /**
     * Gets the connector
     * 
     * @return the connector
     */
    public LdapConnector getConnector() {
        return this.connector;
    }

    /**
     * @param delegate {@link LoginDelegate}
     */
    public void setDelegate(LoginDelegate delegate) {
        this.delegate = delegate;
    }

    /**
     * @return o responsável pelo login
     */
    public LoginDelegate getDelegate() {
        // FIXME seiti extrair LoginTemplate para uma interface
        if (this.delegate == null) {
            this.delegate =
                    new LoginDelegate(this.getLdapTemplate(), this.getConnector(), this.getTransactionalController(), this
                            .getTransactionalController().getDAOFactory().getDAOByClass(CredentialDAO.class));
        }
        return this.delegate;
    }

    /**
     * {@inheritDoc}
     */
    public Subject loadSubject(WebSecurityContext context) {
        return HttpApplicationSecurityManager.loadSubjectFromWebSecurityContext(context);
    }

    /**
     * {@inheritDoc}
     */
    public Subject login(String username, String password, WebSecurityContext context) throws SecurityException {
        try {
            Set<Permission> permissions = this.getDelegate().authenticateAndAuthorize(username, password);
            Set<SecurityPrincipal> principals = Collections.singleton(new SecurityPrincipal(username));
            LOG.info(String.format("User %s authenticated and access authorized.", username));
            return new Subject(true, principals, permissions, Collections.emptySet());
        } catch (RuntimeException e) {
            LOG.warn("Exception when authenticating user: " + username, e);
            throw new AuthenticationException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void logout(WebSecurityContext context) {
        context.getRequest().getSession().removeAttribute(SESSION_KEY);
    }

    /**
     * {@inheritDoc}
     */
    public void storeSubject(WebSecurityContext context, Subject subject) {
        HttpApplicationSecurityManager.storeSubjectIntoWebSecurityContext(context, subject);
    }
}
